﻿#pragma once

#include <functional>
#include <map>
#include <shared_mutex>
#include <queue>
#include <unordered_set>

#include "../SDK/CChestBlockActor.h"
#include "../SDK/CClientInstance.h"
#include "../SDK/CGameMode.h"
#include "../SDK/CHIDController.h"
#include "../SDK/CMoveInputHandler.h"
#include "../SDK/CRakNetInstance.h"

struct AABBHasher final {
	constexpr size_t operator()(const AABB i) const {
		return Utils::Utils::PosToHash(toVec3ti(i.lower));
	}
};

class GameData final {
private:
	static C_ClientInstance* clientInstance;
	static C_LocalPlayer* localPlayer;
	static C_GameMode* gameMode;
	static C_EntityList* entityList;
	static C_RakNetInstance* raknetInstance;
	static C_HIDController* hidController;

	static std::unordered_set<AABB, AABBHasher> chestList;
	static std::shared_mutex chestListMutex;

	static float fov;
	static int fps;
	static int frameCount;
	static int cpsLeft;
	static int cpsRight;
	static int leftclickCount;
	static int rightclickCount;

public:
	static int64_t lastUpdate;
	static std::u8string getScreenName;

private:
	static bool shouldTerminateB;
	static bool shouldHideB;
	static TextHolder fakeName;

public:
	// static bool IsExecutableAddress(LPVOID pAddress) ;

	static bool canUseMoveKeys();
	static bool isKeyDown(int key);
	static bool isKeyPressed(int key);
	static bool isRightClickDown();
	static bool isLeftClickDown();
	static bool isWheelDown();
	static bool shouldTerminate();
	static bool shouldHide();
	static void hide();
	static void terminate();
	static void updateGameData(C_GameMode* gameMode);

	static void addChestToList(C_ChestBlockActor* ChestBlock2);
	static void EntityList_tick(C_EntityList* list);
	static void setHIDController(C_HIDController* Hid);
	static void setRakNetInstance(C_RakNetInstance* raknet);

	static uintptr_t retrieveClientInstance();

	inline static void clearChestList() {
		[[maybe_unused]] auto lock{std::shared_lock<std::shared_mutex>(chestListMutex)};
		chestList.clear();
	}

	inline static C_ClientInstance* getClientInstance() { return clientInstance; };
	inline static C_GuiData* getGuiData() { return clientInstance->getGuiData(); };
	inline static C_LocalPlayer* getLocalPlayer() {
		localPlayer = clientInstance->getLocalPlayer();

		if (localPlayer == nullptr) [[unlikely]]
			gameMode = nullptr;
		return localPlayer;
	};
	inline static C_LocalPlayer** getPtrLocalPlayer() {
		return &localPlayer;
	};
	inline static bool isInGame() noexcept {
		return localPlayer != nullptr;
	}

	inline static C_GameMode* getCGameMode() noexcept { return gameMode; }
	inline static C_EntityList* getEntityList() noexcept { return entityList; }
	inline static C_HIDController** getHIDController() noexcept { return &hidController; }
	inline static C_RakNetInstance* getRakNetInstance() noexcept { return raknetInstance; }
	inline static std::unordered_set<AABB, AABBHasher>& getChestList() noexcept { return chestList; }
	inline static auto lockChestList() noexcept { return std::shared_lock<std::shared_mutex>(chestListMutex); }
	inline static void setFakeName(TextHolder& name) noexcept { fakeName = name; }
	inline static TextHolder& getFakeName() noexcept { return fakeName; }
	static void forEachEntity(std::function<void(C_Entity*)>);
	// void forEachAlive(std::function<void(C_Entity*)>) const ;
	inline static int getFPS() noexcept { return fps; }
	inline static int getLeftCPS() noexcept { return cpsLeft; }
	inline static int getRightCPS() noexcept { return cpsRight; }
	inline static bool getIsWorldBuilder() noexcept { return isInGame() && localPlayer->isWorldBuilder(); }
};

namespace fast_io {
class displayClientMessage_device {
public:
	using char_type = char8_t;
};

template <std::contiguous_iterator Iter>
constexpr void write(displayClientMessage_device, Iter begin, Iter end) {
	[[assume(end >= begin)]];	
	C_GuiData::displayClientMessage(reinterpret_cast<const char8_t*>(begin), static_cast<size_t>(end - begin));
}

class sendServiceMessage_device {
public:
	using char_type = char8_t;

public:
	static std::u8string name;
	static std::u8string xboxid;
};

template <std::contiguous_iterator Iter>
constexpr void write(sendServiceMessage_device sd, Iter begin, Iter end) {
	[[assume(end >= begin)]];
	if (auto localPlayer = GameData::getLocalPlayer(); localPlayer != nullptr) {
		static C_TextPacket_view textPacket(sd.name, sd.xboxid);
		static auto packetsender = GameData::getClientInstance()->loopbackPacketSender;
		if (sd.name.empty() || sd.xboxid.empty()) [[unlikely]] {
			sd.name = localPlayer->getNameTag()->getString();
			sd.xboxid = fast_io::u8concat(localPlayer->getXuid());
		}
		textPacket.message.setText(reinterpret_cast<const char8_t*>(begin), static_cast<size_t>(end - begin));
		packetsender->sendToServer(&textPacket);
	}
	
}
}  // namespace fast_io

extern fast_io::displayClientMessage_device GameChatBar;
extern fast_io::sendServiceMessage_device ServiceMessage;